<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Snake Game</title>
    <style>
        html, body {
            margin: 0px;
            height: 100%;
            width: 100%;
            display: flex;
            justify-content: center; /* 水平居中（如果需要的话） */
            align-items: center; /* 垂直居中 */

        }

        .page-content {
            padding: 20px 15px; /* 内边距（可选） */
            background-color: #f9f9f9; /* 背景色（可选） */
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); /* 阴影效果（可选） */
            display: flex; /* 启用Flexbox布局 */
            justify-content: center; /* 水平居中 */
            align-items: center; /* 垂直居中（在这个例子中可能不是必需的，但展示了如何垂直对齐） */
        }

        .item {
            margin: 0 5px; /* 可选：为canvas元素之间添加一些间距 */
            width: 480px;
            height: 480px;
            border: 5px solid rgba(255, 0, 84, 0.51);
        }

        .match-content {
            width: 480px;
            height: 480px;
            /*display: flex; !* 启用Flexbox布局 *!*/
            /*justify-content: center; !* 水平居中 *!*/
            /*align-items: center; !* 垂直居中（在这个例子中可能不是必需的，但展示了如何垂直对齐） *!*/
        }

        .content-div {
            height: 50px;
            line-height: 50px;
            display: flex; /* 启用Flexbox布局 */
            align-items: center;
            justify-content: center;
        }

        #no_div {
            margin-top: 50px;
            font-weight: bold;
        }

        #tips {
            color: red;
            font-size: 12px;
        }

        #left_two, #right_three {
            width: 480px;
            height: 480px;
            position: absolute; /* 使 canvas 元素定位 */
            z-index: 1; /* 默认 z-index 值，可按需调整 */
            /* 设定透明度，例如 50% 不透明 */
            background-color: rgba(234, 29, 29, 0.5);
            top: 0px;
            left: 0px;
            display: flex;
            flex-direction: column;
            align-items: center;
            /*justify-content: center;*/
            text-align: center;
        }

        #left, #right {
            position: absolute; /* 使 canvas 元素定位 */
            z-index: 2; /* 默认 z-index 值，可按需调整 */
            background-color: #f9f9f9; /* 背景色（可选） */
        }

        #left_score_message, #right_score_message {
            width: 100%; /* 可根据需要调整宽度 */
            max-width: 480px; /* 限制最大宽度，保持居中效果 */
            margin: 200px 0px 25px 0px; /* 添加一些垂直间距 */
        }

        #reset {
            width: 100%; /* 可根据需要调整宽度 */
            max-width: 480px; /* 限制最大宽度，保持居中效果 */
        }

        #left_score_message, #right_score_message {
            color: #ffffff;
            font-weight: bold;
            font-size: 50px;
        }

        #right_two {
            position: absolute; /* 使 canvas 元素定位 */
            z-index: 3; /* 默认 z-index 值，可按需调整 */
            background-color: #f9f9f9; /* 背景色（可选） */
        }

        #countdown {
            width: 480px;
            height: 480px;
            z-index: 0;
            background-color: rgba(0, 0, 0, 0);
            color: black;
            font-weight: bold;
            font-size: 75px;
            text-align: center;
            line-height: 480px;
        }

    </style>
</head>
<body>
<div class="page-content" style="position: relative">
    <div class="item" style="position: relative;">
        <canvas id="left" width="480" height="480"></canvas>
        <div id="left_two">
            <div id="left_score_message"></div>
            <div id="reset">
                <button id="reset_btn">再来一局</button>
                &nbsp;
                <button id="back_btn">退出</button>
            </div>
        </div>
    </div>
    <div class="item" style="position: relative;">
        <canvas id="right" width="480" height="480"></canvas>
        <div id="right_two" class="match-content">
            <div id="no_div" class="content-div"></div>
            <div id="two_div" class="content-div">
                <input id="no" type="text" placeholder="请输入对方账号"/>&nbsp;
                <button id="match">PK</button>
            </div>
            <div class="content-div">
                <span id="tips"></span>
            </div>
            <div class="content-div" id="pk_div">
                <button id="accept">接受</button>
                &nbsp;
                <button id="reject">拒绝</button>
            </div>
        </div>
        <div id="right_three">
            <div id="right_score_message">123</div>
        </div>
    </div>
    <div id="countdown" style="position: absolute">
    </div>
</div>
<script>
    window.addEventListener("DOMContentLoaded", () => {
            /**
             * 左边的画布
             */
            const left_canvas = document.getElementById('left');
            /**
             * 左边的画笔
             */
            const left_ctx = left_canvas.getContext('2d');
            /**
             * 右边的画布
             */
            const right_canvas = document.getElementById('right');
            /**
             * 右边的画笔
             */
            const right_ctx = right_canvas.getContext('2d');
            /**
             * 单元格大小
             */
            const grid_size = 20;
            /**
             * 使用 Math.ceil 来确保整数行数
             */
            const rows = Math.ceil(left_canvas.width / grid_size);
            /**
             * 使用 Math.ceil 来确保整数列数
             */
            const cols = Math.ceil(left_canvas.height / grid_size);
            /**
             * 蛇的起始位置
             */
            let left_snake = [{x: 0, y: 0}];
            let right_snake = [{x: 0, y: 0}];
            /**
             * 食物默认位置
             */
            let left_food = {x: 0, y: 0};
            let right_food = {x: 0, y: 0};
            /**
             * 初始化食物随机位置
             */
            placeFood();
            /**
             * 基础速度
             */
            let base_speed = 150;
            /**
             * 是否自动寻路
             */
            let is_auto = false;
            /**
             * x方向增量
             */
            let left_x = 0;
            /**
             * y方向增量
             */
            let left_y = 0;

            /**
             * 对方的账号
             */
            let pk_id = '';
            /**
             * 是否对右边绘制
             */
            let is_draw_right = false;
            /**
             * 是否把我的数据同步给对方
             */
            let sync_data = false;
            /**
             * 是否允许用户操作键盘
             */
            let is_run = true;
            /**
             * 没人发起PK的时候隐藏接收和拒绝按钮
             */
            document.getElementById('pk_div').style.visibility = 'hidden';


            /**
             * 创建一个WebSocket对象并连接到服务器
             */
            const socket = new WebSocket('ws://snake.smallboxa.top:8765')
            /**
             *接收到消息时触发
             */
            socket.onmessage = function (event) {
                console.log('Message from server ', event.data);
                let json = JSON.parse(event.data);
                if (json.type == 'init') {
                    document.getElementById("no_div").innerHTML = json.data;
                } else if (json.type == 'pk') {
                    pk_id = json.data.id;
                    document.getElementById("tips").innerHTML = "PK邀请,账号:" + json.data.id + ",倒计时:" + json.data.second
                    document.getElementById('pk_div').style.visibility = 'visible';
                } else if (json.type == 'tips') {
                    document.getElementById("tips").innerHTML = json.data;
                } else if (json.type == 'status') {
                    if (json.data == 'idle') {
                        document.getElementById("tips").innerHTML = '';
                        document.getElementById('pk_div').style.visibility = 'hidden';
                    } else if (json.data == 'prepare') {
                        socket.send(JSON.stringify({
                            "type": "ready",
                            "data": {"id": pk_id, "snake": left_snake, "food": left_food}
                        }));
                    } else if (json.data == 'go') {
                        document.getElementById('countdown').innerText = "GO";
                        document.getElementById('countdown').style.zIndex = "4";
                        setTimeout(() => {
                            let countdown = document.getElementById('countdown');
                            countdown.innerText = "GO";
                            countdown.style.zIndex = "-1";
                            left_x = grid_size
                            sync_data = true;
                            is_run = true;
                        }, 500);
                    } else if (json.data == 'busy') {
                        is_draw_right = true;
                        //清除提示信息
                        document.getElementById("tips").innerHTML = '';
                        //展示画布
                        document.getElementById('right_three').style.zIndex = "1";
                        document.getElementById('right_two').style.zIndex = "2";
                        document.getElementById('right').style.zIndex = "3";
                        //展示画布
                        document.getElementById('right_three').style.zIndex = "1";
                        document.getElementById('right_two').style.zIndex = "2";
                        document.getElementById('right').style.zIndex = "3";
                    } else if (json.data == 'exit') {
                        is_run = true;
                        resetGame()
                        pk_id = '';
                        document.getElementById('left_two').style.zIndex = "1";
                        document.getElementById('left').style.zIndex = "2";
                        document.getElementById('right').style.zIndex = "1";
                        document.getElementById('right_three').style.zIndex = "2";
                        document.getElementById('right_two').style.zIndex = "3";
                        document.getElementById("no").innerHTML = '';
                    }
                } else if (json.type == 'data') {
                    right_snake = json.data.snake;
                    right_food = json.data.food;
                    right_ctx.clearRect(0, 0, left_canvas.width, left_canvas.height);
                    drawTableLine(right_ctx);
                    draw_snake_food(right_canvas, right_ctx, right_snake, right_food);
                } else if (json.type == 'over') {
                    document.getElementById('right_score_message').innerHTML = '游戏结束,得分:' + (json.data);
                    document.getElementById('right_three').style.zIndex = "3";
                    document.getElementById('right').style.zIndex = "2";
                    document.getElementById('right_two').style.zIndex = "1";
                    is_draw_right = false;
                } else if (json.type == 'ready') {
                    let countdown = document.getElementById('countdown');
                    countdown.innerText = json.data;
                    countdown.style.zIndex = "10";
                }
            };
            /**
             *链接关闭时候触发
             */
            socket.onclose = function (event) {
                let is_draw_right = false;
                let sync_data = false;
                console.log('WebSocket is closed now.');
            };
            /**
             * 链接错误时候触发
             */
            socket.onerror = function (error) {
                let is_draw_right = false;
                let sync_data = false;
                console.error('WebSocket Error: ', error);
            };


            /**
             * 给PK按钮添加点击事件监听器
             */
            let button = document.getElementById('match');
            button.addEventListener('click', function () {
                if (!is_run) {
                    resetGame();
                }
                let no = document.getElementById('no');
                if (!no.value) {
                    return;
                }
                pk_id = no.value;
                socket.send(JSON.stringify({"type": "connect", "data": no.value}));
                is_run = false;
                resetGame()
                document.getElementById('left_two').style.zIndex = "1";
                document.getElementById('left').style.zIndex = "2";
                button.blur()
            });
            /**
             * 接收按钮事件
             */
            let accept = document.getElementById('accept');
            accept.addEventListener('click', function () {
                if (!is_run) {
                    resetGame()
                }
                socket.send(JSON.stringify({
                    "type": "accept",
                    "data": {"id": pk_id}
                }));
                let pk_div = document.getElementById('pk_div');
                pk_div.style.visibility = 'hidden';
                is_run = false;
                resetGame()
                accept.blur()
            });
            /**
             * 拒绝按钮事件
             */
            let reject = document.getElementById('reject');
            reject.addEventListener('click', function () {
                socket.send(JSON.stringify({"type": "reject", "data": {"id": pk_id}}));
                let pk_div = document.getElementById('pk_div');
                pk_div.style.visibility = 'hidden';
                reject.blur()
            });
            /**
             * 再来一局
             */
            let reset_btn = document.getElementById('reset_btn');
            reset_btn.addEventListener('click', function () {
                //需要等待对面也结束以后才可以
                if (is_draw_right) {
                    alert("需要等对方结束");
                    return;
                }
                resetGame();
                document.getElementById('left_two').style.zIndex = "1";
                document.getElementById('left').style.zIndex = "2";
                if (pk_id) {
                    socket.send(JSON.stringify({
                        "type": "ready",
                        "data": {"id": pk_id, "snake": left_snake, "food": left_food}
                    }));
                } else {
                    is_run = true;
                }
                reset_btn.blur();
            });
            /**
             * 退出
             */
            let back_btn = document.getElementById('back_btn');
            back_btn.addEventListener('click', function () {
                //不重置游戏
                document.getElementById('left_two').style.zIndex = "1";
                document.getElementById('left').style.zIndex = "2";
                document.getElementById('right').style.zIndex = "1";
                document.getElementById('right_three').style.zIndex = "2";
                document.getElementById('right_two').style.zIndex = "3";
                document.getElementById("no").innerHTML = '';
                if (pk_id) {
                    socket.send(JSON.stringify({
                        "type": "exit",
                        "data": {"id": pk_id}
                    }));
                }
                pk_id = '';
                back_btn.blur()
            });

            /**
             * 绘制表格
             * @param temp_ctx
             */
            function drawTableLine(temp_ctx) {
                temp_ctx.strokeStyle = 'rgba(255, 0, 84, 0.51)';
                temp_ctx.lineWidth = 1; // 线条宽度
                for (let x = grid_size; x < left_canvas.width; x += grid_size) {
                    // 画一条直线
                    temp_ctx.beginPath();
                    temp_ctx.moveTo(x, 0); // 起点坐标(x, y)
                    temp_ctx.lineTo(x, left_canvas.height); // 终点坐标(x, y)
                    temp_ctx.stroke(); // 绘制线条
                }
                for (let y = grid_size; y < left_canvas.height; y += grid_size) {
                    // 画一条直线
                    temp_ctx.beginPath();
                    temp_ctx.moveTo(0, y); // 起点坐标(x, y)
                    temp_ctx.lineTo(left_canvas.width, y); // 终点坐标(x, y)
                    temp_ctx.stroke(); // 绘制线条
                }
            }

            /**
             * 主流程方法
             */
            function main() {
                //默认可以直接操作,如果联机的话需要等待服务器命令
                left_ctx.clearRect(0, 0, left_canvas.width, left_canvas.height);
                drawTableLine(left_ctx)
                if (is_run) {
                    advanceSnake();
                    if (sync_data && socket.readyState == WebSocket.OPEN) {
                        socket.send(JSON.stringify({
                            "type": "data",
                            "data": {"id": pk_id, "snake": left_snake, "food": left_food}
                        }));
                    }
                }
                draw_snake_food(left_canvas, left_ctx, left_snake, left_food);
                let speed = base_speed - left_snake.length / 10;
                setTimeout(main, speed);
            }

            class Rect {
                constructor(parentRect, x, y, g, tx, ty) {
                    this.parentRect = parentRect;
                    this.g = g;
                    this.x = x;
                    this.y = y;
                    this.h = Math.abs(this.x - tx) + Math.abs(this.y - ty);
                    this.f = this.g + this.h;
                }

                eq(rect) {
                    return this.x == rect.x && this.y == rect.y;
                }
            }

            /**
             * 寻找路径 近
             */
            function find_rect_near(map, x, y, tx, ty, cal) {
                const directions = [{dx: -1, dy: 0}, {dx: 1, dy: 0}, {dx: 0, dy: -1}, {dx: 0, dy: 1}];
                let temp_rect = new Rect(null, x, y, 0, tx, ty);
                let open_list = [temp_rect];
                let close_list = [];
                while (temp_rect.x != tx || temp_rect.y != ty) {
                    let temp_map = map.map(row => row.slice());

                    if (cal) {
                        let last_index = 1;
                        let rect = {...temp_rect.parentRect}
                        while (rect.parentRect != null && last_index <= left_snake.length) {
                            temp_map[left_snake[left_snake.length - last_index].x / grid_size]
                                [left_snake[left_snake.length - last_index].y / grid_size] = 0;
                            rect = {...rect.parentRect}
                            last_index++;
                            console.log(last_index)
                        }
                    }

                    let temp_open_list = [];
                    const indexToRemove = open_list.findIndex(obj => obj.eq(temp_rect));
                    // 如果找到了该对象，则使用 splice 方法删除它
                    if (indexToRemove !== -1) {
                        open_list.splice(indexToRemove, 1);
                    }
                    close_list.push(temp_rect);
                    directions.forEach(({dx, dy}) => {
                        const new_x = temp_rect.x + dx * grid_size;
                        const new_y = temp_rect.y + dy * grid_size;
                        const new_x_index = new_x / grid_size;
                        const new_y_index = new_y / grid_size;
                        // 检查新位置是否在有效范围内且可通过
                        if ((dx === -1 ? new_x >= 0 : true) &&
                            (dx === 1 ? new_x <= left_canvas.width - grid_size : true) &&
                            (dy === -1 ? new_y >= 0 : true) &&
                            (dy === 1 ? new_y <= left_canvas.height - grid_size : true) &&
                            temp_map[new_x_index][new_y_index]) {
                            const new_rect = new Rect(temp_rect, new_x, new_y, temp_rect.g + grid_size, tx, ty);
                            // 检查 new_rect 是否在 close_list 中
                            if (!close_list.some(item => item.eq(new_rect))) {
                                // 在 open_list 中查找 new_rect
                                const found_item = open_list.find(item => item.eq(new_rect));
                                // 如果找到了，并且 g 值更优，则更新父节点和 g 值，并添加到 temp_open_list
                                if (found_item) {
                                    if (found_item.g > new_rect.g + grid_size) {
                                        found_item.parentRect = temp_rect;
                                        found_item.g = temp_rect.g + grid_size;
                                    }
                                    temp_open_list.push(found_item);
                                } else {
                                    // 如果没有找到，则直接将 new_rect 添加到 temp_open_list
                                    temp_open_list.push(new_rect);
                                }
                            }
                        }
                    });
                    if (temp_open_list.length === 0) {
                        if (open_list.length === 0) {
                            return {is_found: false, rect: temp_rect};
                        } else {
                            temp_rect = open_list.reduce((minObj, currentObj) => {
                                return (currentObj['f'] < minObj['f']) ? currentObj :
                                    (currentObj['f'] > minObj['f']) ? minObj :
                                        (currentObj['h'] < minObj['h']) ? currentObj : minObj;
                            }, open_list[0]);
                        }
                    } else {
                        const min_f = Math.min(...temp_open_list.map(obj => obj['f']));
                        // 然后使用 filter 方法返回所有 f 属性等于 maxF 的元素
                        const filter_list = temp_open_list.filter(obj => obj['f'] === min_f);


                        // filter_list 现在包含所有 f 属性最大的元素
                        if (filter_list.length > 1) {
                            if (temp_rect.parentRect == null) {
                                //取蛇的方向
                                if(left_snake.length > 1){
                                    if (left_snake[0].x === left_snake[1].x) {
                                        temp_rect = filter_list.find(element => element.x === left_snake[0].x);
                                    } else {
                                        temp_rect = filter_list.find(element => element.y === left_snake[0].y);
                                    }
                                    if (!temp_rect) {
                                        temp_rect = filter_list.reduce((minObj, currentObj) => {
                                            return currentObj['h'] < minObj['h'] ? currentObj : minObj;
                                        }, filter_list[0]);
                                    }

                                }else {
                                    temp_rect = filter_list.reduce((minObj, currentObj) => {
                                        return currentObj['h'] < minObj['h'] ? currentObj : minObj;
                                    }, filter_list[0]);
                                }
                            } else {
                                let c_x = temp_rect.x;
                                let p_x = temp_rect.parentRect.x;
                                if (c_x === p_x) {
                                    temp_rect = filter_list.find(element => element.x === c_x);
                                } else {
                                    temp_rect = filter_list.find(element => element.y === temp_rect.y);
                                }
                                if (!temp_rect) {
                                    temp_rect = filter_list.reduce((n, c) => {
                                        return n['h'] < c['h'] ? n : c;
                                    }, filter_list[0])
                                }
                            }
                        } else {
                            temp_rect = filter_list[0];
                        }
                        temp_open_list.forEach(item => {
                            if (!open_list.some(l => l.eq(item))) {
                                open_list.push(item);
                            }
                        });
                    }
                }
                return {is_found: true, rect: temp_rect};
            }

            /**
             * 寻找路径 远
             */
            function find_rect_far(map, x, y, tx, ty) {
                const directions = [{dx: -1, dy: 0}, {dx: 1, dy: 0}, {dx: 0, dy: -1}, {dx: 0, dy: 1}];
                let temp_rect = new Rect(null, x, y, 0, tx, ty);
                let open_list = [temp_rect];
                let close_list = [];
                while (temp_rect.x != tx || temp_rect.y != ty) {
                    let temp_open_list = [];
                    const indexToRemove = open_list.findIndex(obj => obj.eq(temp_rect));
                    // 如果找到了该对象，则使用 splice 方法删除它
                    if (indexToRemove !== -1) {
                        open_list.splice(indexToRemove, 1);
                    }
                    close_list.push(temp_rect);
                    directions.forEach(({dx, dy}) => {
                        const new_x = temp_rect.x + dx * grid_size;
                        const new_y = temp_rect.y + dy * grid_size;
                        const new_x_index = new_x / grid_size;
                        const new_y_index = new_y / grid_size;
                        // 检查新位置是否在有效范围内且可通过
                        if ((dx === -1 ? new_x >= 0 : true) &&
                            (dx === 1 ? new_x <= left_canvas.width - grid_size : true) &&
                            (dy === -1 ? new_y >= 0 : true) &&
                            (dy === 1 ? new_y <= left_canvas.height - grid_size : true) &&
                            map[new_x_index][new_y_index]) {
                            const new_rect = new Rect(temp_rect, new_x, new_y, temp_rect.g + grid_size, tx, ty);
                            // 检查 new_rect 是否在 close_list 中
                            if (!close_list.some(item => item.eq(new_rect))) {
                                temp_open_list.push(new_rect);
                            }
                        }
                    });
                    if (temp_open_list.length === 0) {
                        if (open_list.length === 0) {
                            return {is_found: false, rect: temp_rect};
                        } else {
                            temp_rect = open_list.reduce((minObj, currentObj) => {
                                return (currentObj['f'] > minObj['f']) ? currentObj :
                                    (currentObj['f'] > minObj['f']) ? minObj :
                                        (currentObj['h'] > minObj['h']) ? currentObj : minObj;
                            }, open_list[0]);
                        }
                    } else {
                        const max_f = Math.max(...temp_open_list.map(obj => obj['f']));
                        // 然后使用 filter 方法返回所有 f 属性等于 maxF 的元素
                        const filter_list = temp_open_list.filter(obj => obj['f'] === max_f);
                        // filter_list 现在包含所有 f 属性最大的元素
                        if (filter_list.length > 1) {
                            if (temp_rect.parentRect == null) {
                                temp_rect = filter_list.reduce((maxObj, currentObj) => {
                                    return currentObj['h'] > maxObj['h'] ? currentObj : maxObj;
                                }, filter_list[0]);
                            } else {
                                let c_x = temp_rect.x;
                                let p_x = temp_rect.parentRect.x;
                                if (c_x === p_x) {
                                    temp_rect = filter_list.find(element => element.x === c_x);
                                } else {
                                    temp_rect = filter_list.find(element => element.y === temp_rect.y);
                                }
                                if (!temp_rect) {
                                    temp_rect = filter_list.reduce((n, c) => {
                                        return n['h'] > c['h'] ? n : c;
                                    }, filter_list[0])
                                }
                            }
                        } else {
                            temp_rect = filter_list[0];
                        }
                    }
                    temp_open_list.forEach(item => {
                        if (!open_list.some(l => l.eq(item))) {
                            open_list.push(item);
                        }
                    });

                }
                return {is_found: true, rect: temp_rect};
            }


            /**
             * 解析路径 并绘制步骤
             */
            function parse_path(find_result) {
                let path = [];
                let current = {...find_result.rect};  // 创建 find_result 的副本
                while (current.parentRect != null) {
                    path.push({...current});  // 将当前副本添加到路径中
                    current = current.parentRect;  // 移动到父级
                    if (current) {
                        current = {...current};  // 同样创建当前父级的副本
                    }
                }
                let next = path[path.length - 1]
                return {next: next, path: path};
            }

            function auto() {
                // 初始化二维数组，并填充默认值（这里使用 0 作为示例）
                let map = Array.from({length: rows}, () => Array(cols).fill(1));
                //标记不可以走的位置
                left_snake.slice(0, -1).forEach((node) => {
                    map[node.x / grid_size][node.y / grid_size] = 0;
                });
                if(left_snake.length == 2){
                    map[left_snake[1].x / grid_size][left_snake[1].y / grid_size] = 0;
                }

                let next = null;
                if (left_snake.length < ((24 * 24) / 4) * 2) {
                    //判断当前位置是否可以找到食物 找到食物返回路径
                    let find_result = find_rect_near(map, left_snake[0].x, left_snake[0].y, left_food.x, left_food.y, false)
                    let find_path = parse_path(find_result);
                    if (find_result.is_found) {
                        //可以找到食物 判断吃到食物以后是否还可以找到尾巴
                        let map_temp = Array.from({length: rows}, () => Array(cols).fill(1));
                        let temp_snake = [];
                        if (find_path.path.length > (left_snake.length + 1)) {
                            find_path.path.slice(0, (left_snake.length + 1) - find_path.path.length).forEach(l => {
                                temp_snake.push({x: l.x, y: l.y});
                            })
                        } else {
                            find_path.path.forEach(l => {
                                temp_snake.push({x: l.x, y: l.y});
                            })
                            left_snake.slice(0, (left_snake.length + 1) - find_path.path.length).forEach(l => {
                                temp_snake.push({x: l.x, y: l.y});
                            })
                        }
                        temp_snake.slice(0, -1).forEach(item => {
                            map_temp[item.x / grid_size][item.y / grid_size] = 0;
                        })
                        if(left_snake.length <= 2){
                            next = find_path.next;
                        }else {
                            let temp_find_result = find_rect_near(map_temp, temp_snake[0].x, temp_snake[0].y,
                                temp_snake[temp_snake.length - 1].x, temp_snake[temp_snake.length - 1].y, false)
                            if (left_snake.length < 2 || temp_find_result.is_found) {
                                //吃到食物以后还可以找到尾巴
                                next = find_path.next;
                            }
                        }

                    }
                }
                if (next == null) {
                    //找不到食物 开始找尾巴
                    let tail_result = find_rect_far(map, left_snake[0].x, left_snake[0].y, left_snake[left_snake.length - 1].x, left_snake[left_snake.length - 1].y)
                    let tail_path = parse_path(tail_result);
                    next = tail_path.next;
                    console.log(3 + ' ' + tail_result.is_found)
                }
                if (next != null) {
                    left_x = 0;
                    left_y = 0;
                    if (next.x > left_snake[0].x) {
                        left_x = grid_size;
                    } else if (next.x < left_snake[0].x) {
                        left_x = -grid_size;
                    } else if (next.y > left_snake[0].y) {
                        left_y = grid_size;
                    } else if (next.y < left_snake[0].y) {
                        left_y = -grid_size;
                    }
                }


            }


            /**
             * 蛇前进的方法
             */
            function advanceSnake() {
                if(is_auto){
                    auto();
                }
                // 下一步要走的位置
                const head = {x: left_snake[0].x + left_x, y: left_snake[0].y + left_y};
                if (isGameOver(head)) {
                    handleOver();
                    return
                }
                left_snake.unshift(head);
                if (head.x === left_food.x && head.y === left_food.y) {
                    placeFood();
                } else {
                    left_snake.pop();
                }
            }

            /**
             * 处理结束事件
             */
            function handleOver() {
                //展示游戏分数  和再来一句 退出按钮
                showSettlePage('left', 'left_two', 'left_score_message', left_snake.length - 1);
                //发送over命令
                if (sync_data && socket.readyState == WebSocket.OPEN) {
                    socket.send(JSON.stringify({
                        "type": "over",
                        "data": {"id": pk_id, "score": left_snake.length - 1}
                    }));
                }
                //停止同步数据
                sync_data = false;
                is_run = false;
            }

            /**
             * 展示结算页面
             */
            function showSettlePage(canvas, page, mes, score) {
                let score_message = document.getElementById(mes);
                score_message.innerHTML = '游戏结束,得分:' + score;
                let left_two = document.getElementById(page);
                left_two.style.zIndex = "2";
                let left = document.getElementById(canvas);
                left.style.zIndex = "1";
            }

            /**
             * 检测下一步是否发生碰撞
             */
            function isGameOver(head) {
                return (
                    head.x < 0 || head.x >= left_canvas.width || head.y < 0 || head.y >= left_canvas.height ||
                    left_snake.slice(0, left_snake.length - 1).some(segment => segment.x === head.x && segment.y === head.y) ||
                    left_snake.length === 24 * 24
                );
            }

            /**
             * 重置蛇和食物的位置
             */
            function resetGame() {
                left_snake = [{x: 0, y: 0}];
                left_x = 0;
                left_y = 0;
                placeFood();
            }

            /**
             * 随机生成食物,排除蛇的坐标
             */
            function placeFood() {
                let foods_arr = [];
                for (let x = 0; x < left_canvas.width; x += 20) {
                    for (let y = 0; y < left_canvas.height; y += 20) {

                        let is_exist = false;
                        for (let i = 0; i < left_snake.length; i++) {
                            if (left_snake[i].x == x && left_snake[i].y == y) {
                                is_exist = true;
                                break;
                            }
                        }
                        if (!is_exist) {
                            foods_arr.push({x: x, y: y})
                        }
                    }
                }
                const randomIndex = Math.floor(Math.random() * foods_arr.length);
                if (foods_arr.length > 0) {
                    left_food.x = foods_arr[randomIndex].x;
                    left_food.y = foods_arr[randomIndex].y;
                }
            }

            /**
             * 绘制蛇和食物
             * @param canvas
             * @param ctx
             * @param snake
             * @param food
             */
            function draw_snake_food(canvas, ctx, snake, food) {
                ctx.fillStyle = 'rgba(255, 192, 203, 0.5)';
                ctx.fillRect(0, snake[0].y, canvas.width, grid_size)
                ctx.fillRect(snake[0].x, 0, grid_size, canvas.height)
                ctx.fillStyle = 'red';
                ctx.fillRect(food.x, food.y, grid_size, grid_size);
                ctx.fillStyle = 'green';
                snake.forEach((segment, index) => {
                    // 检查是否是第一个方块
                    if (index === 0) {
                        ctx.fillStyle = 'green'; // 如果是第一个方块，则设置为红色
                    } else {
                        ctx.fillStyle = 'rgba(77,189,71,1)';
                    }
                    ctx.fillRect(segment.x, segment.y, grid_size, grid_size); // 绘制方块

                    if(index > 0){
                        ctx.fillStyle = 'rgb(255,255,255)';
                        let pre = snake[index-1];
                        let cur = snake[index];
                        let next = snake[index+1];
                        //x都相等 横线
                        //y都相等 竖线
                        //拐弯
                        ctx.beginPath();
                        if (!next){
                            if(pre.x === cur.x && pre.y > cur.y){
                                ctx.moveTo(segment.x + grid_size/2, segment.y+grid_size);
                                ctx.lineTo(segment.x + grid_size/2, segment.y+grid_size/2);
                            }else if(pre.x === cur.x && pre.y < cur.y) {
                                ctx.moveTo(segment.x+grid_size/2, segment.y);
                                ctx.lineTo(segment.x + grid_size/2, segment.y+grid_size/2);
                            }else if(pre.y === cur.y && pre.x > cur.x) {
                                ctx.moveTo(segment.x+grid_size, segment.y+grid_size/2);
                                ctx.lineTo(segment.x + grid_size/2, segment.y+grid_size/2);
                            }else if(pre.y === cur.y && pre.x < cur.x) {
                                ctx.moveTo(segment.x, segment.y+grid_size/2);
                                ctx.lineTo(segment.x + grid_size/2, segment.y+grid_size/2);
                            }
                        } else if(pre.x === cur.x && (!next || cur.x === next.x)){
                            ctx.moveTo(segment.x + grid_size/2, segment.y);
                            ctx.lineTo(segment.x + grid_size/2, segment.y+grid_size);
                        }else if (pre.y === cur.y && (!next || cur.y === next.y)){
                            ctx.moveTo(segment.x, segment.y+grid_size/2);
                            ctx.lineTo(segment.x + grid_size, segment.y+grid_size/2);
                        }else if (next){
                            if (
                                (pre.x === cur.x && cur.y === next.y && pre.y < cur.y && cur.x > next.x) ||
                                (pre.y === cur.y && cur.x === next.x && pre.x < cur.x && cur.y > next.y)
                            ){
                                ctx.moveTo(segment.x + grid_size/2, segment.y);
                                ctx.lineTo(segment.x + grid_size/2, segment.y+grid_size/2);
                                ctx.moveTo(segment.x + grid_size/2, segment.y+grid_size/2);
                                ctx.lineTo(segment.x, segment.y+grid_size/2);
                            }else if (
                                (pre.x === cur.x && cur.y === next.y && pre.y < cur.y && cur.x < next.x) ||
                                (pre.y === cur.y && cur.x === next.x && pre.x > cur.x && cur.y > next.y)
                            ){
                                ctx.moveTo(segment.x + grid_size/2, segment.y);
                                ctx.lineTo(segment.x + grid_size/2, segment.y+grid_size/2);
                                ctx.moveTo(segment.x + grid_size/2, segment.y+grid_size/2);
                                ctx.lineTo(segment.x + grid_size, segment.y+grid_size/2);
                            }else if (
                                (pre.x === cur.x && cur.y === next.y && pre.y > cur.y && cur.x > next.x) ||
                                (pre.y === cur.y && cur.x === next.x && pre.x < cur.x && cur.y < next.y)
                            ){
                                ctx.moveTo(segment.x, segment.y+grid_size/2);
                                ctx.lineTo(segment.x + grid_size/2, segment.y+grid_size/2);
                                ctx.moveTo(segment.x + grid_size/2, segment.y+grid_size/2);
                                ctx.lineTo(segment.x + grid_size/2, segment.y+grid_size);
                            }else if (
                                (pre.x === cur.x && cur.y === next.y && pre.y > cur.y && cur.x < next.x) ||
                                (pre.y === cur.y && cur.x === next.x && pre.x > cur.x && cur.y < next.y)
                            ){
                                ctx.moveTo(segment.x + grid_size, segment.y+grid_size/2);
                                ctx.lineTo(segment.x + grid_size/2, segment.y+grid_size/2);
                                ctx.moveTo(segment.x + grid_size/2, segment.y+grid_size/2);
                                ctx.lineTo(segment.x + grid_size/2, segment.y+grid_size);
                            }

                        }
                        ctx.stroke();
                    }

                });
            }

            /**
             * 监听键盘按下
             */
            document.addEventListener('keydown', event => {
                const keyPressed = event.key;
                switch (keyPressed) {
                    case 'ArrowUp':
                    case 'w':
                    case 'W':
                        if (left_y !== grid_size && (left_snake.length == 1 || left_snake[0].y <= left_snake[1].y)) {
                            left_x = 0;
                            left_y = -grid_size;
                        }
                        break;
                    case 'ArrowDown':
                    case 's':
                    case 'S':
                        if (left_y !== -grid_size && (left_snake.length == 1 || left_snake[0].y >= left_snake[1].y)) {
                            left_x = 0;
                            left_y = grid_size;
                        }
                        break;
                    case 'ArrowLeft':
                    case 'a':
                    case 'A':
                        if (left_x !== grid_size && (left_snake.length == 1 || left_snake[0].x <= left_snake[1].x)) {
                            left_x = -grid_size;
                            left_y = 0;
                        }
                        break;
                    case 'ArrowRight':
                    case 'd':
                    case 'D':
                        if (left_x !== -grid_size && (left_snake.length == 1 || left_snake[0].x >= left_snake[1].x)) {
                            left_x = grid_size;
                            left_y = 0;
                        }
                        break;
                    case ' ':
                        base_speed = 75;
                        break;
                    case '{':
                        is_auto = true;
                        break;
                    case '}':
                        is_auto = false;
                        break;
                }
            });
            /**
             * 监听键盘弹起
             */
            document.addEventListener('keyup', event => {
                const keyPressed = event.key;
                switch (keyPressed) {
                    case ' ':
                        base_speed = 150;
                        break;
                }
            });

            main();
        }
    )
    ;
</script>
</body>
</html>
